package com.polymathcoder.dpastry.core.config;

import com.polymathcoder.dpastry.api.config.Configuration;
import com.polymathcoder.dpastry.api.config.ConfigurationException;
import com.polymathcoder.dpastry.core.network.TcpTransportService;
import com.polymathcoder.dpastry.dPastry;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;

import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 * User: Drew
 * Date: Jul 7, 2010
 * Time: 11:46:38 AM
 * To change this template use File | Settings | File Templates.
 */
@Root(name = "network")
public class TcpTransportConfiguration implements Configuration<TcpTransportService> {

    // *************** PRIVATE FIELDS ***************

    private dPastry.ServiceHandleImpl<TcpTransportService> handle = null;

    @ElementList(name = "bind-addresses")
    private List<IpBinding> bindings = new ArrayList<IpBinding>();

    @ElementList(name = "bootstrap-nodes")
    private List<BootstrapNode> bootNodes = new ArrayList<BootstrapNode>();

    // *************** ACCESSOR METHODS ***************

    public List<BootstrapNode> getBootstrapNodes() {
        return bootNodes;
    }

    public List<IpBinding> getIpBindings() {
        return bindings;
    }

    public dPastry.ServiceHandleImpl<TcpTransportService> getHandle() {
        return handle;
    }

    public void setHandle(dPastry.ServiceHandleImpl<TcpTransportService> handle) {
        this.handle = handle;
    }

    // *************** NESTED CONFIG CLASSES ***************

    @Root
    public final static class BootstrapNode {

        @Attribute
        private String address = null;

        @Attribute
        private int port = 57101;

        public BootstrapNode(String address, int port) {
            this.address = address;
            this.port = port;
        }

        public BootstrapNode() {
        }

        public String getAddress() {
            return address;
        }

        public void setAddress(String address) {
            this.address = address;
        }

        public int getPort() {
            return port;
        }

        public void setPort(int port) {
            this.port = port;
        }
    }

    @Root
    public final static class IpBinding {

        @Attribute(required = false)
        private String address = null;

        @Attribute
        private int port = 57101;

        public IpBinding(String address, int port) {
            this.address = address;
            this.port = port;
        }

        public IpBinding() {
        }

        public String getAddress() {
            return address;
        }

        public void setAddress(String address) {
            this.address = address;
        }

        public int getPort() {
            return port;
        }

        public void setPort(int port) {
            this.port = port;
        }
    }

    // *************** STATIC METHODS ***************

    /**
     * Static method to return the default configuration. This can be used as the starting point when only minor changes
     * are being made to the configuration. It is recommended that significantly different configurations be stored in
     * custom xml files.
     *
     * @return a TcpTransportConfiguration object containing the default configuration
     * @throws ConfigurationException throws a ConfigurationException if anything goes wrong
     */
    public static TcpTransportConfiguration getDefaultConfiguration() throws ConfigurationException {
        return fromInputStream(TcpTransportConfiguration.class.getResourceAsStream("TcpTransportConfiguration.xml"));
    }

    /**
     * Static method to return the configuration specified by the target of the given File object.
     *
     * @param f the file object from which to load the configuration XML
     * @return a TcpTransportConfiguration object containing the specified configuration data
     * @throws com.polymathcoder.dpastry.api.config.ConfigurationException
     *          throws a ConfigurationException if anything goes wrong
     */
    public static TcpTransportConfiguration fromFile(File f) throws ConfigurationException {
        Serializer serializer = new Persister();
        try {
            return serializer.read(TcpTransportConfiguration.class, f);
        } catch (Exception e) {
            throw new ConfigurationException(e);
        }
    }

    /**
     * Static method to return the configuration specified by the target of the given InputStream.
     * This method is provided as a convenience so that one can use Class.getResourceAsStream(...) as
     * the source of the configuration XML.
     *
     * @param s the InputStream from which to load the configuration XML
     * @return a TcpTransportConfiguration object containing the specified configuration data
     * @throws ConfigurationException throws a ConfigurationException if anything goes wrong
     */
    public static TcpTransportConfiguration fromInputStream(InputStream s) throws ConfigurationException {
        Serializer serializer = new Persister();
        try {
            return serializer.read(TcpTransportConfiguration.class, s);
        } catch (Exception e) {
            throw new ConfigurationException(e);
        }
    }

    /**
     * Static method to save the given TcpTransportConfiguration to a File object.
     *
     * @param cfg the TcpTransportConfiguration to save
     * @param f   the target File
     * @return the saved TcpTransportConfiguration if successful
     * @throws ConfigurationException throws a ConfigurationException if anything goes wrong
     */
    public static TcpTransportConfiguration toFile(TcpTransportConfiguration cfg, File f) throws ConfigurationException {
        Serializer serializer = new Persister();
        try {
            serializer.write(cfg, f);
            return cfg;
        } catch (Exception e) {
            throw new ConfigurationException(e);
        }
    }

}
